"""Contains classes for character specific data, like attributes,
skills, wallet, and corporation. Parsing makes use of the great
cElementTree module availble standard in Python 2.5."""

import xml.etree.cElementTree as ET

ATTRIBUTES = (
        "memory",
        "perception",
        "intelligence",
        "willpower",
        "charisma",
        )

class Data():
    """Base class for grouping data fields together, generally based on
what XML file the data is stored in."""
    def __init__(self, fields):
        self.data = {}
        self.fields = fields
        # Initialize just so the keys are in there
        for field in self.fields:
            self.data[field] = ""

    def __getattr__(self, name):
        """Override to make accessing Data object's data fields easier."""
        if name in self.fields:
            return self.data[name]
        else:
            raise KeyError, "Invalid field %s requested from %s object" % \
                (name, self.__class__.__name__)

    def checkXML(self, tree):
        """Checks if the XML tree contains an error and raises an
        Exception if so."""

        # cElementTree doesn't return None, dumb
        if tree.find("error") is not None:
            elem = tree.find("error")
            raise Exception, "%s: %s" % (elem.get("code"), elem.text)

    def __repr__(self):
        return self.data.__repr__()

    def __str__(self):
        return '\n'.join(map(lambda x, y: "%s: %s" % (x, y),
            self.data.keys(), self.data.values()))



class Character(Data):
    """Stores data about a single character, e.g. name, race, skills, and
attributes."""
    def __init__(self, fin):
        fields = (
                "characterID",
                "name",
                "race",
                "bloodLine",
                "gender",
                "corporationName",
                "corporationID",
                "balance",
                "attributeEnhancers",
                "attributes",
                "skills",
                "skillInTraining",
                "params",
        )
        Data.__init__(self, fields)

        self.data["attributeEnhancers"] = {}
        self.data["attributes"] = {}
        self.data["skills"] = []
        # skills will be a SkillTree
        # skillInTraining needs to come from another XML file.

        self.parseCS(fin)


    def parseCS(self, fin):
        """Takes a filename as an argument and returns
a Character object of the parsed data."""
        tree = ET.parse(fin)
        self.checkXML(tree)
        tree = tree.find("result")

        for field in self.fields:
            if field == "attributeEnhancers":
                for attr in ATTRIBUTES:
                    bonus = tree.find("attributeEnhancers")\
                        .find(attr+"Bonus")
                    if bonus:
                        self.data["attributeEnhancers"][attr] = \
                            bonus.find("augmentatorValue").text
                    else:
                        self.data["attributeEnhancers"][attr] = "0"
            elif field == "attributes":
                for attr in ATTRIBUTES:
                    self.data["attributes"][attr] = \
                        tree.find("attributes").find(attr).text
            elif field == "skills":
                rows = tree.find("rowset")
                for row in rows.getchildren():
                    self.data["skills"].append(CharSkill(row))
            elif field == "skillInTraining":
                pass # handled by parseSIT()
            elif field == "params":
                pass # handled elsewhere
            else:
                self.data[field] = tree.find(field).text


    def parseSIT(self, fin):
        """Parses the SkillInTraining.xml file into a SkillInTraining
        object."""
        tree = ET.parse(fin)
        self.checkXML(tree)
        tree = tree.find("result")

        self.data["skillInTraining"] = SkillInTraining(tree)


class CharSkill(Data):
    """Represents a skill that has been trained by a character. Static
skill information like description, name, and group are found in Skill,
not CharSkill.

Argument is a cElementTree.Element."""
    def __init__(self, elem):
        fields = (
                "typeID",
                "skillpoints",
                "level"
        )
        Data.__init__(self, fields)

        for field in self.fields:
            self.data[field] = elem.get(field)


class SkillInTraining(Data):
    """Stores data about the currently training skill for a character."""
    def __init__(self, elem):
        fields = (
                "trainingEndTime",
                "trainingStartTime",
                "trainingTypeID",
                "trainingStartSP",
                "trainingDestinationSP",
                "trainingToLevel",
                "skillInTraining",
        )
        Data.__init__(self, fields)

        for field in self.fields:
            self.data[field] = elem.find(field).text


class SkillTree():
    """Represents the entire skill tree for Eve Online. Skills can be
fetched from the SkillTree either by typeID or groupID."""
    def __init__(self, fin):
        self.types = {} # typeID : Skill
        self.groups = {} # groupID : SkillGroup
        self.parse(fin)

    def parse(self, fin):
        """Parses the SkillTree.xml file to populate the typeID -> Skill
        and groupID -> SkillGroup dictionaries."""
        tree = ET.parse(fin).find("result").find("rowset")
        for group in tree.findall("row"):
            # Parse in the SkillGroups, which will populate self.types
            self.groups[group.get("groupID")] = SkillGroup(self, group)


class SkillGroup():
    """Wrapper for a group of skills, e.g. Mechanic, Navigation, Gunnery."""
    def __init__(self, stree, group):
        self.group_id = group.get("groupID")
        self.group_name = group.get("groupName")
        self.skills = [] # List of Skills

        for skill in group.find("rowset"):
            skill_obj = Skill(skill)
            self.skills.append(skill_obj)
            stree.types[skill.get("typeID")] = skill_obj


class Skill(Data):
    """Stores data about a single skill, e.g. name, description, prereqs."""
    def __init__(self, skill):
        fields = (
                "typeName",
                "description",
                "rank",
                "requiredSkills",
                "requiredAttributes",
                "skillBonusCollection",
        )
        Data.__init__(self, fields)

        self.data["requiredSkills"] = []
        self.data["requiredAttributes"] = ()
        self.data["skillBonusCollection"] = []

        for field in self.fields:
            if field == "typeName":
                self.data[field] = skill.get(field)
            elif field =="description":
                self.data[field] = skill.find(field).text
            elif field == "requiredAttributes":
                attrs = skill.find(field)
                self.data[field] = ( attrs.find("primaryAttribute").text,
                        attrs.find("secondaryAttribute").text )
            else: # parse the rowsets: requiredSkills, skillBonusCollection
                for rowset in skill.findall("rowset"):
                    if rowset.get("name") == field:
                        if field == "requiredSkills":
                            for row in rowset:
                                self.data[field].append( (row.get("typeID"),
                                    row.get("skillLevel")) )
                        if field == "skillBonusCollection":
                            for row in rowset.findall("row"):
                                self.data[field].append( (
                                    row.get("bonusType"),
                                    row.get("bonusValue")
                            ) )
